package org.emet.jee.console.services;

import java.util.Calendar;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.emet.jee.BusinessException;
import org.emet.jee.console.dao.SequenceDao;
import org.emet.jee.console.entity.sys.Sequence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 简单的序列号生存器。不适用于集群环境下。
 */
@Service
@Transactional
public class SequenceGenarator {
	private static final Logger logger = LoggerFactory.getLogger(SequenceGenarator.class);

	@Autowired
	private SequenceDao sequenceDao;

	private static Lock lock = new ReentrantLock();

	/**
	 * TODO make it as a distributed generator
	 * 
	 */
	public String generate(Long seqId) {
		lock.lock();
		try {
			Sequence seq = sequenceDao.get(seqId);
			return generateSequence(seq);
		} catch (Exception e) {
			final String message = "生成序列号{}的时候发生错误 :{}";
			logger.error(message, seqId, e);
			throw new BusinessException(message, e);
		} finally {
			lock.unlock();
		}
	}

	private String generateSequence(Sequence seq) {
		Calendar cal = Calendar.getInstance();
		int year = cal.get(Calendar.YEAR);
		int month = cal.get(Calendar.MONTH) + 1;
		int day = cal.get(Calendar.DAY_OF_MONTH);

		Sequence nextSeq = getNextSequence(seq, month);
		return buildSequenceString(year, month, day, nextSeq.getSeqNo());
	}

	private Sequence getNextSequence(Sequence seq, int month) {
		if (isFirstDayMonth(seq.getMonth(), month)) {
			seq.setMonth(month);
			seq.setSeqNo(1);
		} else {
			final long seqNo = seq.getSeqNo();
			seq.setSeqNo(seqNo + 1);
		}
		return seq;
	}

	private String buildSequenceString(int year, int month, int day, long newSeq) {
		final StringBuilder sb = new StringBuilder();
		sb.append(year);
		sb.append(month);
		sb.append(day);
		sb.append(newSeq);
		return sb.toString();
	}

	private boolean isFirstDayMonth(int monthInDB, int month) {
		return monthInDB != month;
	}
}
